﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GmAlignmentActionBehavior.h"
#include "IActionInputEventArgs.h"
#include "IUiView.h"
#include "IUiDocument.h"
#include "IDocument.h"
#include "IUserTransaction.h"
#include "IModelView.h"
#include "GbmpAlignmentUtils.h"
#include "INotificationDetailItem.h"
#include "INotificationManager.h"
#include "INotification.h"
#include "IControlDefinition.h"
#include "ILineEditDefinition.h"
#include "IPickTarget.h"
#include "IReferencePlane.h"
#include "GbmpModelViewTypes.h"
#include "IPlane.h"
#include "Coordinate3d.h"
#include "GbmpAllowedElementJudgerForPickFilter.h"
#include "IPickFilter.h"
#include "IGraphicsPoint.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsBRepFace.h"
#include "IEditMode.h"
#include "IGraphicsLine.h"
#include "IGraphicsNodeReference.h"
#include "IInstance.h"
#include "Vector3dUtils.h"
#include "ICylinder.h"
#include "IGraphicsArc.h"
#include "ITrimmedSurface.h"
#include "IFace.h"
#include "GUiBaseInterfaceEnums.h"
#include "IBehaviorConfig.h"
#include "GbmpFileSystem.h"
#include "ILinkDocumentElement.h"
#include "IGenericElement.h"
#include "IDrawingViewport.h"
#include "IGraphicsPolyCurve.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

#define ACTION_EDIT_DISTANCE                    L"gmEditDistance"
#define ACTION_CHECKBOX_KEEP_JOINING            L"gmCheckBoxKeepJoining"



namespace
{
    template<typename T>
    class AllowedElementJudgerForAlignmentPickFilter : public IGbmpAllowedElementJudgerForPickFilter
    {
    public:
        virtual ~AllowedElementJudgerForAlignmentPickFilter() {}
        virtual bool IsAllowedElement(const IElement *pElement) const override
        {
            if (const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement))
            {
                return IsA<T>(pGenericElement->GetExternalObject());
            }
            return IsA<T>(pElement);
        }
    };

    OwnerPtr<ICylinder> GetGmICylinderFromGface(const gcmp::IGraphicsBRepFace *pGFace)
    {
        if (pGFace == nullptr)
            return nullptr;

        if (!pGFace->IsCylindric())
            return nullptr;

        const IFace* pIFace = pGFace->GetGmFace();
        if (pIFace == nullptr)
            return nullptr;

        const ITrimmedSurface* pTrimmedSurface = pIFace->GetTrimmedSurface();
        if (pTrimmedSurface == nullptr || !pTrimmedSurface->IsCylindric())
            return nullptr;

        return TransferOwnershipCast<ICylinder>(pTrimmedSurface->GetUnderlyingSurface());
    }

    class GbmpAlignmentPickFilter : public IPickFilter
    {
    public:
        GbmpAlignmentPickFilter(gcmp::IDocument* pDocument);
        ~GbmpAlignmentPickFilter();
        virtual bool AllowElement(const gcmp::ElementId& id) const override;
        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override;
        virtual bool SetPickTargetOption(gcmp::IPickTarget* pickTarget);
        //设置工作平面法向量
        void SetWorkPlaneNormal(const gcmp::Vector3d& workPlaneNormal);
        //设置被拾取过的GNodeReference
        void SetPickedNodeReferences(const gcmp::GraphicsNodeReferenceOwnerPtrVector& pickedNodeReferences);
        //设置是否允许拾取所有类型的Element
        void SetIsAllowAllElement(bool isEnable);
        //设置是否是在草图环境下
        void SetIsSketchMode(bool isEnable);
        //设置能拾取的类型（点、线、面、边）
        void SetPickTarget(gcmp::OwnerPtr<gcmp::IPickTarget> pickTarget);

        template<typename T>
        void AddAllowElementType()
        {
            m_arrAllowElementType.push_back(NEW_AS_OWNER_PTR(gcmp::AllowedElementJudgerForAlignmentPickFilter<T>));
        }
    protected:
        const IDocument* GetDocument() const { return m_pDocument; }

    private:
        const IDocument *m_pDocument;

    private:
        gcmp::Vector3d m_workPlaneNormal;   //工作平面的法线
        gcmp::OwnerPtr<gcmp::IPickTarget> m_pickTargetOut;   //能拾取的类型
        gcmp::GraphicsNodeReferenceOwnerPtrVector m_pickedNodeReferences; //过滤之前已经拾取到的对象，避免重复拾取
        std::vector<gcmp::OwnerPtr<gcmp::IGbmpAllowedElementJudgerForPickFilter>> m_arrAllowElementType;  //支持拾取的Element类型
        bool m_isAllowAllElement;
        bool m_isSketchMode;
    };

    GbmpAlignmentPickFilter::GbmpAlignmentPickFilter(gcmp::IDocument* pDocument)
        : m_pDocument(pDocument)
        , m_isAllowAllElement(false)
        , m_isSketchMode(false)
        , m_pickTargetOut(TransferOwnership(gcmp::IPickTarget::Create()))
    {
        m_pickTargetOut->DisableAll();
        m_pickTargetOut->EnableCurve();
        m_pickTargetOut->EnableFace();
        m_pickTargetOut->EnableEndPoint();
        m_pickTargetOut->EnableCenterPoint();
        m_pickTargetOut->EnablePoint();
    }

    GbmpAlignmentPickFilter::~GbmpAlignmentPickFilter(void)
    {
    }

    bool GbmpAlignmentPickFilter::AllowElement(const gcmp::ElementId& elementID) const
    {
        // 视口不支持对齐操作
        const IDocument* pDoc = GetDocument();
        if (pDoc)
        {
            const IElement* pElement = pDoc->GetElement(elementID);
            if (const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement))
            {
                const IDrawingViewport* pViewport = quick_cast<IDrawingViewport>(pGenericElement->GetExternalObject());
                if (pViewport)
                {
                    return false;
                }
            }
        }

        if (m_isAllowAllElement)
        {
            return true;
        }
        else
        {
            const gcmp::IDocument* pDbDoc = GetDocument();
            const IElement* pElement = pDbDoc->GetElement(elementID);
            if (const ILinkDocumentElement * pLinkDocumentElement = quick_cast<ILinkDocumentElement>(pElement))
            {
                return false;
            }

            const IEditMode* pEditModeUE = IEditMode::GetTopActiveEditMode(GetDocument());

            if (pElement && !m_arrAllowElementType.empty())
            {
                FOR_EACH(pTypeJudger, m_arrAllowElementType)
                {
                    DBG_WARN_AND_CONTINUE_UNLESS(pTypeJudger, L"pTypeJudger为空",L"GDMPLab",L"2024-03-30");
                    if (pTypeJudger->IsAllowedElement(pElement))
                    {
                        return true;
                    }
                }
                return false;
            }
            return true;
        }
    }
    bool GbmpAlignmentPickFilter::AllowGraphicsNode(const IGraphicsNodeReference& curNodeReference) const
    {
        //不允许重复选择同一个物体
        if (!m_pickedNodeReferences.empty())
        {
            if (curNodeReference.GetElementId() == m_pickedNodeReferences[0]->GetElementId())
            {
                return false;
            }
        }
        OwnerPtr<GraphicsNodeAndTransform> opCurTransNode = curNodeReference.GetIntersectGraphicsNode();
        if (opCurTransNode == nullptr || opCurTransNode->m_wpGraphicsNode.Get() == nullptr)
            return false;

        const Matrix4d* pCurTransformMat = opCurTransNode->m_opTransfrom.get();
        const IGraphicsNode* pCurNode = opCurTransNode->m_wpGraphicsNode.Get();
        const IGraphicsLine* pCurGLine = quick_cast<IGraphicsLine>(pCurNode);

        const gcmp::IDocument* pDbDoc = GetDocument();
        IElement* pCurElement = pDbDoc->GetElement(curNodeReference.GetElementId());
        if (IsA<IInstance>(pCurElement))
        {
            if (curNodeReference.GetGraphicsNodeReferenceType() != GraphicsNodeReferenceType::GraphicsNodeSelf)
            {
                return false;
            }
        }
        if (pCurGLine)
        {
            //拾取直线的情况
            //如果是第一次选取，任何情况都可以被拾取到
            //如果是第二次选取，要分以下几种情况
            if (!m_pickedNodeReferences.empty())
            {
                ElementId firstElementID = m_pickedNodeReferences.at(0)->GetElementId();
                IElement* pElement1 = pDbDoc->GetElement(firstElementID);

                OwnerPtr<GraphicsNodeAndTransform> opTransNode1 = m_pickedNodeReferences.at(0)->GetIntersectGraphicsNode();
                if (opTransNode1 == nullptr || opTransNode1->m_wpGraphicsNode.Get() == nullptr)
                    return false;

                const IGraphicsNode* pPickedGNode1 = opTransNode1->m_wpGraphicsNode.Get();
                const Matrix4d* pTransformMat1 = opTransNode1->m_opTransfrom.get();
                const IGraphicsArc* pGArc1 = dynamic_cast<const IGraphicsArc*>(pPickedGNode1);
                const IGraphicsLine* pGLine1 = quick_cast<IGraphicsLine>(pPickedGNode1);
                const IGraphicsBRepFace* pGFace1 = dynamic_cast<const IGraphicsBRepFace*>(pPickedGNode1);

                GraphicsNodeReferenceType firstType = m_pickedNodeReferences.at(0)->GetGraphicsNodeReferenceType();
                GraphicsNodeReferenceType secondType = curNodeReference.GetGraphicsNodeReferenceType();
                //如果第一次拾取到的是圆弧，那么第二次拾取到直线的时候只能是点。
                if (pGArc1 && firstType == GraphicsNodeReferenceType::GraphicsNodeSelf && secondType == GraphicsNodeReferenceType::GraphicsNodeSelf)
                {
                    return false;
                }
                //如果第一次拾取到的直线与第二次要拾取的直线垂直时，只能拾取第二条线上的端点
                else if (pGLine1 && firstType == GraphicsNodeReferenceType::GraphicsNodeSelf && secondType == GraphicsNodeReferenceType::GraphicsNodeSelf)
                {
                    Vector3d firstLineDir = pGLine1->GetEndPoint() - pGLine1->GetStartPoint();
                    Vector3d secondLineDir = pCurGLine->GetEndPoint() - pCurGLine->GetStartPoint();
                    if (pCurTransformMat != nullptr)
                        secondLineDir = Vector3dUtils::TransformVector(secondLineDir, *pCurTransformMat);
                    if (pTransformMat1 != nullptr)
                        firstLineDir = Vector3dUtils::TransformVector(firstLineDir, *pTransformMat1);
                    if (Vector3dUtils::IsPerpendicular(firstLineDir, secondLineDir))
                    {
                        return false;
                    }
                }
                else if (secondType == GraphicsNodeReferenceType::EndPoint || secondType == GraphicsNodeReferenceType::StartPoint)
                {
                    ElementId elementId = curNodeReference.GetElementId();
                    IReferencePlane* pRefPlane = quick_cast<IReferencePlane>(pDbDoc->GetElement(elementId));
                    if (pRefPlane)
                    {
                        return false;
                    }
                }
                //如果第一次拾取到的平面与第二次要拾取的直线垂直时，只能拾取第二条线上的端点
                else if (pGFace1 && firstType == GraphicsNodeReferenceType::GraphicsNodeSelf && secondType == GraphicsNodeReferenceType::GraphicsNodeSelf)
                {
                    Vector3d faceNormal = pGFace1->GetNormal();
                    Vector3d secondLineDir = pCurGLine->GetEndPoint() - pCurGLine->GetStartPoint();

                    if (pCurTransformMat != nullptr)
                        secondLineDir = Vector3dUtils::TransformVector(secondLineDir, *pCurTransformMat);
                    if (pTransformMat1 != nullptr)
                        faceNormal = Vector3dUtils::TransformVector(faceNormal, *pTransformMat1);

                    if (Vector3dUtils::IsParallel(faceNormal, secondLineDir))
                    {
                        return false;
                    }
                }
                //如果第一次拾取到的直线与第二次要拾取的直线平行，只能拾取第二条线本身
                else if (pGLine1 && firstType == GraphicsNodeReferenceType::GraphicsNodeSelf && secondType != GraphicsNodeReferenceType::GraphicsNodeSelf)
                {
                    Vector3d firstLineDir = pGLine1->GetEndPoint() - pGLine1->GetStartPoint();
                    Vector3d secondLineDir = pCurGLine->GetEndPoint() - pCurGLine->GetStartPoint();
                    if (pCurTransformMat != nullptr)
                        secondLineDir = Vector3dUtils::TransformVector(secondLineDir, *pCurTransformMat);
                    if (pTransformMat1 != nullptr)
                        firstLineDir = Vector3dUtils::TransformVector(firstLineDir, *pTransformMat1);
                    if (Vector3dUtils::IsParallel(firstLineDir, secondLineDir))
                    {
                        return false;
                    }
                }
                //如果第一次拾取到的平面与第二次要拾取的直线平行，只能拾取第二条线本身
                else if (pGFace1 && firstType == GraphicsNodeReferenceType::GraphicsNodeSelf && secondType != GraphicsNodeReferenceType::GraphicsNodeSelf)
                {
                    Vector3d faceNormal = pGFace1->GetNormal();
                    Vector3d secondLineDir = pCurGLine->GetEndPoint() - pCurGLine->GetStartPoint();

                    if (pCurTransformMat != nullptr)
                        secondLineDir = Vector3dUtils::TransformVector(secondLineDir, *pCurTransformMat);
                    if (pTransformMat1 != nullptr)
                        faceNormal = Vector3dUtils::TransformVector(faceNormal, *pTransformMat1);

                    if (Vector3dUtils::IsPerpendicular(faceNormal, secondLineDir))
                    {
                        return false;
                    }
                }
                return true;
            }
            else
            {
                if (curNodeReference.GetGraphicsNodeReferenceType() == GraphicsNodeReferenceType::EndPoint ||
                    curNodeReference.GetGraphicsNodeReferenceType() == GraphicsNodeReferenceType::StartPoint)
                {
                    ElementId elementId = curNodeReference.GetElementId();
                    IReferencePlane* pRefPlane = quick_cast<IReferencePlane>(pDbDoc->GetElement(elementId));
                    if (pRefPlane)
                    {
                        return false;
                    }
                }
                return true;
            }
        }

        const IGraphicsArc* pCurGArc = quick_cast<IGraphicsArc>(pCurNode);
        if (pCurGArc)
        {
            if (!m_pickedNodeReferences.empty())
            {
                ElementId firstElementID = m_pickedNodeReferences.at(0)->GetElementId();
                IElement* pElement1 = pDbDoc->GetElement(firstElementID);

                OwnerPtr<GraphicsNodeAndTransform> pPickedTransGNode1 = curNodeReference.GetIntersectGraphicsNode();
                if (pPickedTransGNode1 == nullptr || pPickedTransGNode1->m_wpGraphicsNode.Get() == nullptr)
                    return false;

                const IGraphicsArc* pGArc1 = quick_cast<IGraphicsArc>(pPickedTransGNode1->m_wpGraphicsNode.Get());
                const IGraphicsBRepFace* pGFace1 = dynamic_cast<const IGraphicsBRepFace*>(pPickedTransGNode1->m_wpGraphicsNode.Get());
                if (pGArc1)
                {
                    if (Vector3dUtils::IsParallel(pGArc1->GetNormal(), pCurGArc->GetNormal()))
                        return true;
                    return false;
                }
                else if (pGFace1 != nullptr)
                {
                    OwnerPtr<ICylinder> opCylinder1 = GetGmICylinderFromGface(pGFace1);
                    if (opCylinder1 == nullptr)
                        return false;

                    Vector3d dirCylider1Z = opCylinder1->GetCoordinate().GetZ();
                    if (pPickedTransGNode1->m_opTransfrom.get() != nullptr)
                        dirCylider1Z = Vector3dUtils::TransformVector(dirCylider1Z, *pPickedTransGNode1->m_opTransfrom.get());

                    Vector3d vector3d = pCurGArc->GetNormal();
                    if (Vector3dUtils::IsParallel(vector3d, dirCylider1Z))
                        return true;
                    return false;
                }
            }
            return true;
        }

        const IGraphicsCurve3d* pCurGCurve = dynamic_cast<const IGraphicsCurve3d*>(pCurNode);
        if (pCurGCurve)
        {
            return true;
        }

        const IGraphicsPoint* pCurGPoint = dynamic_cast<const IGraphicsPoint*>(pCurNode);
        if (pCurGPoint)
        {
            return true;
        }

        const IGraphicsPolyCurve* pPolyCurve = dynamic_cast<const IGraphicsPolyCurve*>(pCurNode);
        if (pPolyCurve)
        {
            return true;
        }

        const IGraphicsBRepFace* pCurGFace = dynamic_cast<const IGraphicsBRepFace*>(pCurNode);
        if (pCurGFace)
        {
            if (pCurGFace->IsPlanar())
            {
                Vector3d vCurFaceNorm = pCurGFace->GetNormal();
                if (pCurTransformMat != nullptr)
                    vCurFaceNorm = Vector3dUtils::TransformVector(vCurFaceNorm, *pCurTransformMat);
                //第二次拾取到的平面不能和第一次拾取的垂直
                if (!m_pickedNodeReferences.empty())
                {
                    if (m_isSketchMode)
                    {
                        return false;
                    }
                    ElementId firstElementID = m_pickedNodeReferences.at(0)->GetElementId();
                    IElement* pElement1 = pDbDoc->GetElement(firstElementID);

                    OwnerPtr<GraphicsNodeAndTransform> opTransNode1 = m_pickedNodeReferences.at(0)->GetIntersectGraphicsNode();
                    if (opTransNode1 == nullptr || opTransNode1->m_wpGraphicsNode.Get() == nullptr)
                        return false;

                    const IGraphicsNode* pPickedGNode1 = opTransNode1->m_wpGraphicsNode.Get();
                    const Matrix4d* pTransformMat1 = opTransNode1->m_opTransfrom.get();
                    const IGraphicsBRepFace* pGFace1 = dynamic_cast<const IGraphicsBRepFace*>(pPickedGNode1);
                    IReferencePlane * pReferencePlane1 = dynamic_cast<IReferencePlane *>(pElement1);

                    if (pReferencePlane1)
                    {
                        if (Vector3dUtils::IsPerpendicular(pReferencePlane1->GetGeometryPlane()->GetNormal(), vCurFaceNorm))
                        {
                            return false;
                        }
                    }
                    else if (pGFace1)
                    {
                        if (!pGFace1->IsPlanar())
                            return false;
                        Vector3d vFace1Norm = pGFace1->GetNormal();
                        if (pTransformMat1 != nullptr)
                            vFace1Norm = Vector3dUtils::TransformVector(vFace1Norm, *pTransformMat1);
                        if (Vector3dUtils::IsPerpendicular(vFace1Norm, vCurFaceNorm))
                        {
                            return false;
                        }
                    }
                }
                //与工作平面平行的面不能被选取（非三维视图下）
                double dt = m_workPlaneNormal * vCurFaceNorm;
                if (fabs(dt) < Constants::DOUBLE_EPS)
                {
                    return true;
                }
            }
            else if (pCurGFace->IsCylindric())
            {
                OwnerPtr<ICylinder> opCurCylinder = GetGmICylinderFromGface(pCurGFace);
                if (opCurCylinder == nullptr)
                    return false;

                if (!IsA<IInstance>(pCurElement))
                    return false;

                Vector3d curCylinderDirZ = opCurCylinder->GetCoordinate().GetZ();
                if (opCurTransNode->m_opTransfrom.get() != nullptr)
                    curCylinderDirZ = Vector3dUtils::TransformVector(curCylinderDirZ, *opCurTransNode->m_opTransfrom.get());

                if (!m_pickedNodeReferences.empty())
                {
                    if (m_isSketchMode)
                    {
                        return false;
                    }
                    ElementId firstElementID = m_pickedNodeReferences.at(0)->GetElementId();
                    IElement* pElement1 = pDbDoc->GetElement(firstElementID);

                    OwnerPtr<GraphicsNodeAndTransform> opTransNode1 = m_pickedNodeReferences.at(0)->GetIntersectGraphicsNode();
                    if (opTransNode1 == nullptr || opTransNode1->m_wpGraphicsNode.Get() == nullptr)
                        return false;

                    const IGraphicsNode* pPickedGNode1 = opTransNode1->m_wpGraphicsNode.Get();
                    const Matrix4d* pTransformMat1 = opTransNode1->m_opTransfrom.get();
                    const IGraphicsBRepFace* pGFace1 = dynamic_cast<const IGraphicsBRepFace*>(pPickedGNode1);
                    const IGraphicsArc* pGArc1 = quick_cast<IGraphicsArc>(pPickedGNode1);

                    if (pGFace1 != nullptr)
                    {
                        OwnerPtr<ICylinder> opCylinder1 = GetGmICylinderFromGface(pGFace1);
                        if (opCylinder1 == nullptr)
                            return false;
                        Vector3d cylinder1DirZ = opCylinder1->GetCoordinate().GetZ();
                        if (opTransNode1->m_opTransfrom.get() != nullptr)
                            cylinder1DirZ = Vector3dUtils::TransformVector(cylinder1DirZ, *opTransNode1->m_opTransfrom.get());
                        if (Vector3dUtils::IsParallel(curCylinderDirZ, cylinder1DirZ))
                            return true;
                        return false;
                    }
                    else if (pGArc1)
                    {
                        if (Vector3dUtils::IsParallel(curCylinderDirZ, pGArc1->GetNormal()))
                            return true;
                        return false;
                    }

                    return false;
                }
                return true;
            }
            return false;
        }
        return false;
    }
    bool GbmpAlignmentPickFilter::SetPickTargetOption(IPickTarget* pickTarget)
    {
        pickTarget->Assign(m_pickTargetOut.get());
        return true;
    }

    void GbmpAlignmentPickFilter::SetWorkPlaneNormal(const Vector3d& workPlaneNormal)
    {
        m_workPlaneNormal = workPlaneNormal;
    }

    void GbmpAlignmentPickFilter::SetPickedNodeReferences(const gcmp::GraphicsNodeReferenceOwnerPtrVector& pickedNodeReferences)
    {
        OwnerPtrContainerUtil::CopyItems(m_pickedNodeReferences, pickedNodeReferences);
    }

    void GbmpAlignmentPickFilter::SetIsAllowAllElement(bool isEnable)
    {
        m_isAllowAllElement = isEnable;
    }

    void GbmpAlignmentPickFilter::SetIsSketchMode(bool isEnable)
    {
        m_isSketchMode = isEnable;
    }

    void GbmpAlignmentPickFilter::SetPickTarget(gcmp::OwnerPtr<gcmp::IPickTarget> pickTarget)
    {
        m_pickTargetOut = TransferOwnership(pickTarget);
    }
}

double GmAlignmentActionBehavior::S_distance = 0.0;
GmAlignmentActionBehavior::GmAlignmentActionBehavior()
    :m_keepJoining(true)
{
    if (IBehaviorConfig::GetConfigRecordValueAsString(L"KeepPreEditValue") == L"true")
    {
        m_distance = GmAlignmentActionBehavior::S_distance;
    }
    else
    {
        m_distance = 0.0;
    }
}

GmAlignmentActionBehavior::~GmAlignmentActionBehavior()
{
    if (IBehaviorConfig::GetConfigRecordValueAsString(L"KeepPreEditValue") == L"true")
    {
        GmAlignmentActionBehavior::S_distance = m_distance;
    }
}

bool GmAlignmentActionBehavior::DefineInputDialog(std::wstring &title, std::vector<OwnerPtr<IControlDefinition>>& inputItems)
{
    title = GBMP_TR(L"对齐");
    OwnerPtr<ILineEditDefinition> opLineEditDef = ILineEditDefinition::Create(ACTION_EDIT_DISTANCE, GBMP_TR(L"对齐后距离(仅直线型构件有效)"), LineEditInputType::InputDouble);
    //对话框的小数部分显示精度，需要将来统一定义并尽量简化
    //std::wostringstream woss;
    //woss << std::fixed << std::setprecision(precision) << t;
    int nDlgPrecision = 3;
    opLineEditDef->SetText(StringUtil::ToWString(m_distance, nDlgPrecision));
    inputItems.push_back(TransferOwnership(opLineEditDef));

    OwnerPtr<ICheckBoxGroupDefinition> opCheckBoxGroupDef = ICheckBoxGroupDefinition::Create(L"SetCheckBoxKeepJoining", GBMP_TR(L"保持端部连接约束组"));
    m_wpCheckBoxGroupDefKeepJoin = opCheckBoxGroupDef.get();
    opCheckBoxGroupDef->AddCheckBox(ACTION_CHECKBOX_KEEP_JOINING, GBMP_TR(L"保持端部连接约束"), GBMP_TR(L"保持端部连接约束"), CheckState::Checked);
    CheckState checkState = m_keepJoining ? CheckState::Checked : CheckState::Unchecked;
    opCheckBoxGroupDef->SetCheckState(ACTION_CHECKBOX_KEEP_JOINING, checkState);
    inputItems.push_back(TransferOwnership(opCheckBoxGroupDef));

    return true;
}

void GmAlignmentActionBehavior::OnInputEvent(IActionInputEventArgs* pArgs)
{
    const std::pair<std::wstring, gcmp::Any>& msg = pArgs->GetInputMessage();
    const std::wstring eventId = msg.first;
    gcmp::Any value = msg.second;
    if (eventId == ACTION_EDIT_DISTANCE)
    {
        m_distance = value.cast<double>();
    }
    else if (eventId == ACTION_CHECKBOX_KEEP_JOINING)
    {
        m_keepJoining = value.cast<bool>();
        m_wpCheckBoxGroupDefKeepJoin->SetCheckState(ACTION_CHECKBOX_KEEP_JOINING, m_keepJoining ? CheckState::Checked : CheckState::Unchecked);

    }
}

double GmAlignmentActionBehavior::GetAlignmentDistance()
{
    return m_distance;
}

void GmAlignmentActionBehavior::OnAlignmentFinished(IUiView* pCurrentView, const IGraphicsNodeReference& targetNode, const IGraphicsNodeReference& sourceNode)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView, L"pCurrentView为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView->GetUiDocument(), L"IUiDocument为空",L"GDMPLab",L"2024-03-30");
    IDocument* pDoc = pCurrentView->GetUiDocument()->GetDbDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    IModelView* pModelView = pCurrentView->GetModelView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(pDoc, GBMP_TR(L"对齐"));
    DBG_WARN_AND_RETURN_VOID_UNLESS(ut.get(), L"ut为空",L"GDMPLab",L"2024-03-30");

    const IElement* pElement = pDoc->GetElement(sourceNode.GetElementId());
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");
    ElementId elementIdToBeAligned = pElement->GetGroupId();
    if (IEditMode* pEditMode = IEditMode::GetTopActiveEditMode(pDoc))
    {
        if (pEditMode->GetEditingElementId() == pElement->GetGroupId())
            elementIdToBeAligned = ElementId::InvalidID;
    }
    

    std::wstring strError;
    bool bOk = false;
    if (m_keepJoining)
    {
        bOk = GbmpAlignmentUtils::AlignElementWithJoining(pModelView, targetNode, sourceNode, m_distance, elementIdToBeAligned, strError);
    }
    else
    {
        bOk = GbmpAlignmentUtils::AlignElement(pModelView, targetNode, sourceNode, m_distance, elementIdToBeAligned, strError);
    }

    if (bOk)
    {
        ut->Commit();
    }
    else
    {
        ut->Rollback();
        std::vector<OwnerPtr<INotificationDetailItem>> detailItems;
        INotificationManager::Get()->AddNotification(TransferOwnership(INotification::CreateNotification(NotificationTypes::InfomationNotificationType, strError, std::move(detailItems))));
    }
}

gcmp::OwnerPtr<gcmp::IPickFilter> GmAlignmentActionBehavior::GetElementPickFilter(EnAlignmentStatus ePickStatus, IDocument* pDocument, const GraphicsNodeReferenceOwnerPtrVector &pickedNodeReferences, IUiView* pCurrentView) const
{
    Vector3d workPlaneNormal = Vector3d();;
    if (pCurrentView->GetModelView()->GetUserDataId() != View3DViewType)

    {
        IModelView* pModelView = pCurrentView->GetModelView();
        if (pModelView)
        {
            OwnerPtr<IPlane> pPlane = pModelView->GetWorkPlane();
            if (pPlane)
                workPlaneNormal = pPlane->GetCoordinate().Z();
        }
    }
    if (ePickStatus == AS_PICK_FIRST)
    {
        OwnerPtr<GbmpAlignmentPickFilter > opPickFilter = NEW_AS_OWNER_PTR(GbmpAlignmentPickFilter, pDocument);
        opPickFilter->SetWorkPlaneNormal(workPlaneNormal);
        opPickFilter->SetIsAllowAllElement(true);
        return TransferOwnership(opPickFilter);
    }
    else if(ePickStatus == AS_PICK_SECOND)
    {
        OwnerPtr<GbmpAlignmentPickFilter > opPickFilter = NEW_AS_OWNER_PTR(GbmpAlignmentPickFilter, pDocument);
        opPickFilter->SetWorkPlaneNormal(workPlaneNormal);
        opPickFilter->SetPickedNodeReferences(pickedNodeReferences);

        const IGraphicsNodeReference* firstNodeReference = pickedNodeReferences[0].get();
        GraphicsNodeReferenceType firstNodeReferenceType = firstNodeReference->GetGraphicsNodeReferenceType();
        opPickFilter->SetIsAllowAllElement(false);
        //如果第一次选的是线的端点，那么第二次只能选点
        if (firstNodeReferenceType == GraphicsNodeReferenceType::EndPoint || firstNodeReferenceType == GraphicsNodeReferenceType::StartPoint)
        {
            gcmp::OwnerPtr<gcmp::IPickTarget> pickTarget = IPickTarget::Create();
            pickTarget->DisableAll();
            pickTarget->EnableEndPoint();
            pickTarget->EnablePoint();
            opPickFilter->SetPickTarget(TransferOwnership(pickTarget));
        }
        return TransferOwnership(opPickFilter);
    }
    return nullptr;
}

void gcmp::GmAlignmentActionBehavior::HandlingPickedNodeReference(IDocument* pDocument, GraphicsNodeReferenceOwnerPtrVector &nodeReferences) const
{
    if (nodeReferences.size() <= 0)
        return;

    ElementId pickedElementID = nodeReferences.at(0)->GetElementId();
    IReferencePlane* referencePlane = quick_cast<IReferencePlane>(pDocument->GetElement(pickedElementID));
    bool bSketchPlane = false;
    if ((referencePlane != nullptr) || bSketchPlane)
    {
        nodeReferences.at(0)->SetGraphicsNodeReferenceType(GraphicsNodeReferenceType::GraphicsNodeSelf);
    }
}

